---
title: "System Programming Basics"
description: "Learn C system programming from a JavaScript perspective. Understand processes, threads, signals, system calls, and compare with JavaScript runtime environment."
---

# System Programming Basics

## 1. Introduction

### From JavaScript Runtime to C System Programming

In JavaScript, you work within a runtime environment that abstracts away system-level operations. In C, you have direct access to the operating system, allowing you to create processes, handle signals, and make system calls.

> 💡 **Key Concept**: C system programming gives you low-level control over the operating system, enabling you to build system utilities, daemons, and high-performance applications.

## 2. Processes and Process Management

<UniversalEditor title="Process Creation" compare={true}>
```javascript !! js
// JavaScript - process management (Node.js)
const { spawn, exec } = require('child_process');

// Spawn a new process
const child = spawn('ls', ['-la'], {
    stdio: 'inherit'
});

child.on('close', (code) => {
    console.log(`Child process exited with code ${code}`);
});

// Execute a command
exec('echo "Hello from child process"', (error, stdout, stderr) => {
    if (error) {
        console.error(`Error: ${error}`);
        return;
    }
    console.log(`Output: ${stdout}`);
});

// Process information
console.log('Process ID:', process.pid);
console.log('Parent Process ID:', process.ppid);
console.log('Platform:', process.platform);
```

```c !! c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

int main() {
    pid_t pid;
    
    printf("Parent process ID: %d\n", getpid());
    
    // Create a new process
    pid = fork();
    
    if (pid < 0) {
        // Fork failed
        perror("Fork failed");
        exit(1);
    } else if (pid == 0) {
        // Child process
        printf("Child process ID: %d\n", getpid());
        printf("Child's parent ID: %d\n", getppid());
        
        // Execute a command
        execlp("ls", "ls", "-la", NULL);
        
        // If execlp returns, there was an error
        perror("execlp failed");
        exit(1);
    } else {
        // Parent process
        printf("Parent waiting for child %d\n", pid);
        
        int status;
        wait(&status);
        
        if (WIFEXITED(status)) {
            printf("Child exited with status %d\n", WEXITSTATUS(status));
        }
    }
    
    return 0;
}
```
</UniversalEditor>

## 3. Threads and Concurrency

<UniversalEditor title="Threading" compare={true}>
```javascript !! js
// JavaScript - threading (Node.js Worker Threads)
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');

if (isMainThread) {
    // Main thread
    const worker = new Worker(__filename, {
        workerData: { number: 42 }
    });
    
    worker.on('message', (result) => {
        console.log('Result from worker:', result);
    });
    
    worker.on('error', (error) => {
        console.error('Worker error:', error);
    });
    
    worker.on('exit', (code) => {
        if (code !== 0) {
            console.error(`Worker stopped with exit code ${code}`);
        }
    });
} else {
    // Worker thread
    const result = workerData.number * 2;
    parentPort.postMessage(result);
}

// Async/await for concurrency
async function concurrentTasks() {
    const promises = [
        new Promise(resolve => setTimeout(() => resolve('Task 1'), 1000)),
        new Promise(resolve => setTimeout(() => resolve('Task 2'), 500)),
        new Promise(resolve => setTimeout(() => resolve('Task 3'), 1500))
    ];
    
    const results = await Promise.all(promises);
    console.log('All tasks completed:', results);
}

concurrentTasks();
```

```c !! c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

// Thread function
void* threadFunction(void* arg) {
    int threadId = *(int*)arg;
    printf("Thread %d started\n", threadId);
    
    // Simulate work
    sleep(1);
    
    printf("Thread %d finished\n", threadId);
    return NULL;
}

// Thread with shared data
int sharedCounter = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* incrementCounter(void* arg) {
    int iterations = *(int*)arg;
    
    for (int i = 0; i < iterations; i++) {
        pthread_mutex_lock(&mutex);
        sharedCounter++;
        pthread_mutex_unlock(&mutex);
    }
    
    return NULL;
}

int main() {
    pthread_t threads[3];
    int threadIds[3] = {1, 2, 3};
    
    // Create threads
    for (int i = 0; i < 3; i++) {
        if (pthread_create(&threads[i], NULL, threadFunction, &threadIds[i]) != 0) {
            perror("Failed to create thread");
            return 1;
        }
    }
    
    // Wait for threads to complete
    for (int i = 0; i < 3; i++) {
        pthread_join(threads[i], NULL);
    }
    
    printf("All threads completed\n");
    
    // Thread with shared data
    pthread_t counterThreads[2];
    int iterations = 1000;
    
    for (int i = 0; i < 2; i++) {
        pthread_create(&counterThreads[i], NULL, incrementCounter, &iterations);
    }
    
    for (int i = 0; i < 2; i++) {
        pthread_join(counterThreads[i], NULL);
    }
    
    printf("Final counter value: %d\n", sharedCounter);
    
    return 0;
}
```
</UniversalEditor>

## 4. Signal Handling

<UniversalEditor title="Signal Handling" compare={true}>
```javascript !! js
// JavaScript - signal handling (Node.js)
process.on('SIGINT', () => {
    console.log('Received SIGINT (Ctrl+C)');
    console.log('Cleaning up...');
    process.exit(0);
});

process.on('SIGTERM', () => {
    console.log('Received SIGTERM');
    console.log('Graceful shutdown...');
    process.exit(0);
});

process.on('uncaughtException', (error) => {
    console.error('Uncaught Exception:', error);
    process.exit(1);
});

// Custom signal handling
let cleanupDone = false;

function cleanup() {
    if (!cleanupDone) {
        console.log('Performing cleanup...');
        cleanupDone = true;
    }
}

process.on('exit', (code) => {
    cleanup();
    console.log(`Process exiting with code ${code}`);
});

// Simulate some work
console.log('Process started. Press Ctrl+C to stop.');
setInterval(() => {
    console.log('Working...');
}, 1000);
```

```c !! c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

volatile sig_atomic_t flag = 0;

// Signal handler function
void signalHandler(int sig) {
    if (sig == SIGINT) {
        printf("\nReceived SIGINT (Ctrl+C)\n");
        printf("Cleaning up...\n");
        flag = 1;
    } else if (sig == SIGTERM) {
        printf("Received SIGTERM\n");
        printf("Graceful shutdown...\n");
        flag = 1;
    }
}

// Cleanup function
void cleanup() {
    printf("Performing cleanup...\n");
    // Add cleanup code here
}

int main() {
    // Set up signal handlers
    if (signal(SIGINT, signalHandler) == SIG_ERR) {
        perror("Failed to set SIGINT handler");
        return 1;
    }
    
    if (signal(SIGTERM, signalHandler) == SIG_ERR) {
        perror("Failed to set SIGTERM handler");
        return 1;
    }
    
    printf("Process started. Press Ctrl+C to stop.\n");
    printf("Process ID: %d\n", getpid());
    
    // Main loop
    while (!flag) {
        printf("Working...\n");
        sleep(1);
    }
    
    cleanup();
    printf("Process exiting\n");
    
    return 0;
}
```
</UniversalEditor>

## 5. System Calls and File Operations

<UniversalEditor title="System Calls" compare={true}>
```javascript !! js
// JavaScript - file system operations (Node.js)
const fs = require('fs');
const path = require('path');

// File operations
fs.writeFileSync('test.txt', 'Hello, World!');
const content = fs.readFileSync('test.txt', 'utf8');
console.log('File content:', content);

// Directory operations
fs.mkdirSync('testdir', { recursive: true });
const files = fs.readdirSync('.');
console.log('Files in current directory:', files);

// File stats
const stats = fs.statSync('test.txt');
console.log('File size:', stats.size);
console.log('Is file:', stats.isFile());
console.log('Is directory:', stats.isDirectory());

// Process information
console.log('Current working directory:', process.cwd());
console.log('Environment variables:', process.env.PATH);

// Cleanup
fs.unlinkSync('test.txt');
fs.rmdirSync('testdir');
```

```c !! c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>

int main() {
    // File operations using system calls
    int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }
    
    const char* message = "Hello, World!";
    ssize_t bytes_written = write(fd, message, strlen(message));
    if (bytes_written == -1) {
        perror("write failed");
        close(fd);
        return 1;
    }
    
    close(fd);
    
    // Read file
    fd = open("test.txt", O_RDONLY);
    if (fd == -1) {
        perror("open for reading failed");
        return 1;
    }
    
    char buffer[100];
    ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
    if (bytes_read > 0) {
        buffer[bytes_read] = '\0';
        printf("File content: %s\n", buffer);
    }
    
    close(fd);
    
    // File stats
    struct stat fileStat;
    if (stat("test.txt", &fileStat) == 0) {
        printf("File size: %ld bytes\n", fileStat.st_size);
        printf("Is file: %s\n", S_ISREG(fileStat.st_mode) ? "Yes" : "No");
        printf("Is directory: %s\n", S_ISDIR(fileStat.st_mode) ? "Yes" : "No");
    }
    
    // Directory operations
    DIR* dir = opendir(".");
    if (dir != NULL) {
        struct dirent* entry;
        printf("Files in current directory:\n");
        while ((entry = readdir(dir)) != NULL) {
            printf("  %s\n", entry->d_name);
        }
        closedir(dir);
    }
    
    // Process information
    char cwd[1024];
    if (getcwd(cwd, sizeof(cwd)) != NULL) {
        printf("Current working directory: %s\n", cwd);
    }
    
    printf("Process ID: %d\n", getpid());
    printf("Parent Process ID: %d\n", getppid());
    
    // Cleanup
    unlink("test.txt");
    
    return 0;
}
```
</UniversalEditor>

## 6. Common Pitfalls
- **Race conditions**: Use proper synchronization mechanisms
- **Signal handler safety**: Only use async-signal-safe functions in signal handlers
- **Resource leaks**: Always close file descriptors and free allocated memory
- **Process zombie**: Properly wait for child processes
- **Thread safety**: Use mutexes and other synchronization primitives

## 7. Exercises
1. Write a program that creates multiple child processes and waits for all to complete.
2. Implement a simple shell that can execute commands.
3. Create a program that handles multiple signals gracefully.
4. Write a multi-threaded program that performs parallel computations.

## 8. Performance Analysis
- **Process creation**: More expensive than thread creation
- **Inter-process communication**: Slower than inter-thread communication
- **System calls**: Overhead compared to user-space operations
- **Signal handling**: Asynchronous and can interrupt normal execution
- **Thread synchronization**: Mutexes and semaphores provide safe concurrent access

---

> **Summary**: C system programming provides direct access to operating system features. Understanding process management, threading, signal handling, and system calls is essential for building system-level applications and utilities. 